﻿using System;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GISCore.Geometry.Partition
{
	//This class represents a grid cell.
	[Serializable]
	public class Cell : ICellable
	{
		public int CellID { get; set; } // each cell has a unique id.
		private List<Line> _lines; // The lines in the cell
        private List<IntersectPoint> _points;// All of the intersect points in the cell
        public List<PostPolygon> postPolys = new List<PostPolygon>();

        /* Neighbour cell id*/
        private int topCell;
        private int bottomCell;
        private int rightCell;
        private int leftCell;

        private List<GridIntersectPoint> _upPoints = new List<GridIntersectPoint>();
        private List<GridIntersectPoint> _downPoints = new List<GridIntersectPoint>();
        private List<GridIntersectPoint> _leftPoints = new List<GridIntersectPoint>();
        private List<GridIntersectPoint> _rightPoints = new List<GridIntersectPoint>();

        public List<LinkedPoint> _upLinkedPoints { get; set; }
        public List<LinkedPoint> _downLinkedPoints { get; set; }
        public List<LinkedPoint> _leftLinkedPoints { get; set; }
        public List<LinkedPoint> _rightLinkedPoints { get; set; }
		

        //Minimum Bounding Rectangle
		public double xmax { get; set; }
		public double ymax { get; set; }

		public double xmin { get; set; }
		public double ymin { get; set; }

        public double Width { get; private set; }
        public double Height { get; private set; }


        public Cell(int id)
        {
            CellID = id;
            _lines = new List<Line>();
        }

		//Initially, only a set of line object is in the cell object.
		public Cell(int id, double width, double height)
            : this(id)
		{			
            Width = width;
            Height = height;
		}

        public Cell(int id, double width, double height, double minX, double maxX, double minY, double maxY)
            : this(id, width, height)
        {
            xmin = minX;
            xmax = maxX;
            ymin = minY;
            ymax = maxY;
        }

        public Cell(int id, double width, double height, Tuple<double, double, double, double> bb)
            : this(id, width, height, bb.Item1, bb.Item2, bb.Item3, bb.Item4) { }


       //Add the grid points in the cell 
        public void setGridIntersectPoint(IEnumerable<GridIntersectPoint> gridPoints,Line line)
        {
            GridIntersectPoint oldPt;
            DulGridIntersectPoint doublePt;
            foreach (var point in gridPoints)
            {
                
                if (point.X == xmin && (point.Y >= ymin && point.Y <= ymax))
                {
                    if (true == _leftPoints.Contains(point))
                    {
                        oldPt = _leftPoints.Find(item => item.X == point.X && item.Y == item.Y);
                        doublePt = new DulGridIntersectPoint(point.X, point.Y);
                        doublePt.start = oldPt.getLine();
                        doublePt.end = line;
                        _leftPoints.Add(doublePt);
                        /* remove the old grid point */
                        _leftPoints.Remove(oldPt);
                    }
                    else
                    {
                        _leftPoints.Add(point);
                    }
                    _leftPoints.Sort();
                }
                else if (point.X == xmax && (point.Y >= ymin && point.Y <= ymax))
                {

                    if (true == _rightPoints.Contains(point))
                    {
                        oldPt = _rightPoints.Find(item => item.X == point.X && item.Y == item.Y);
                        doublePt = new DulGridIntersectPoint(point.X, point.Y);
                        doublePt.start = oldPt.getLine();
                        doublePt.end = line;
                        _rightPoints.Add(doublePt);
                        /* remove the old grid point */
                        _rightPoints.Remove(oldPt);
                    }
                    else
                    {
                        _rightPoints.Add(point);
                    }
                    _rightPoints.Sort();

                }
                else if (point.Y == ymin && (point.X <= xmax && point.X >= xmin))
                {
                    if (true == _downPoints.Contains(point))
                    {
                        oldPt = _downPoints.Find(item => item.X == point.X && item.Y == item.Y);
                        doublePt = new DulGridIntersectPoint(point.X, point.Y);
                        doublePt.start = oldPt.getLine();
                        doublePt.end = line;
                        _downPoints.Add(doublePt);
                        /* remove the old grid point */
                        _downPoints.Remove(oldPt);
                    }
                    else
                    {
                        _downPoints.Add(point);
                    }
                    _downPoints.Sort();

                }
                else if (point.Y == ymax && (point.X <= xmax && point.X >= xmin))
                {

                    if (true == _upPoints.Contains(point))
                    {
                        oldPt = _upPoints.Find(item => item.X == point.X && item.Y == item.Y);
                        doublePt = new DulGridIntersectPoint(point.X, point.Y);
                        doublePt.start = oldPt.getLine();
                        doublePt.end = line;
                        _upPoints.Add(doublePt);
                        /* remove the old grid point */
                        _upPoints.Remove(oldPt);
                    }
                    else
                    {
                        _upPoints.Add(point);
                    }

                    _upPoints.Sort();
                    
                }

            }
        }

        /*Top neighbour of this cell*/
         
        public void setTopCell(int cellId)
        {
            topCell = cellId;
        }
         
          
        /*Bottom neighbour of this cell */
        public void setBottomCell(int cellId)
        {
            bottomCell = cellId;
        }
         

         /* Left neighbour of this cell */ 
        public void setLeftCell(int cellId)
        {
            leftCell = cellId;
        }
         
        /* Right neighbour of this cell */
        public void setRightCell(int cellId)
        {
            rightCell = cellId;
        }

        public void addLine(Line l)
        {
            _lines.Add(l);
        }

		
        public override string ToString()
        {
            return string.Format("CellID {0} => {1} Lines", CellID, _lines.Count);
        }

        /* implement the methods in the interface*/
        public void setIntersectPoint(IEnumerable<IntersectPoint> points)
        {
            _points = new List<IntersectPoint>();
            _points.AddRange(points);
        }

        public void setPostPolygons(IEnumerable<PostPolygon> postPolys)
        {
            throw new NotImplementedException();
        }

        public void setTopGridIntersectPoint(IEnumerable<GridIntersectPoint> points)
        {
            _upPoints = points as List<GridIntersectPoint>;
        }

        public void setBottomGridIntersectPoint(IEnumerable<GridIntersectPoint> points)
        {
            _downPoints = points as List<GridIntersectPoint>;   
        }

        public void setLeftGridIntersectPoint(IEnumerable<GridIntersectPoint> points)
        {
            _leftPoints = points as List<GridIntersectPoint>;
        }

        public void setRightGridIntersectPoint(IEnumerable<GridIntersectPoint> points)
        {
            _rightPoints = points as List<GridIntersectPoint>;
        }

        public IEnumerable<IntersectPoint> getIntersectPoint()
        {
            return _points as List<IntersectPoint>;
        }

        IEnumerable<PostPolygon> ICellable.getPostPolygons()
        {
            return postPolys;
        }

        public IEnumerable<GridIntersectPoint> getTopGridIntersectPoint()
        {
            return _upPoints; 
        }

        public IEnumerable<GridIntersectPoint> getBottomGridIntersectPoint()
        {
            return _downPoints;
        }

        public IEnumerable<GridIntersectPoint> getLeftGridIntersectPoint()
        {
            return _leftPoints;
        }

        public IEnumerable<GridIntersectPoint> getRightGridIntersectPoint()
        {
            return _rightPoints;
        }

        public IEnumerable<LinkedPoint> getTopLinkedGridIntersectPoint()
        {
            return _upLinkedPoints;
        }

        public IEnumerable<LinkedPoint> getBottomLinkedGridIntersectPoint()
        {
            return _downLinkedPoints;
        }

        public IEnumerable<LinkedPoint> getLeftLinkedGridIntersectPoint()
        {
            return _leftLinkedPoints;
        }

        public IEnumerable<LinkedPoint> getRightLinkedGridIntersectPoint()
        {
            return _rightLinkedPoints;
        }

        public IEnumerable<Line> getLines()
        {
            return _lines;
        }
        
        /*
        public void setGridIntersectPoint(IEnumerable<GridIntersectPoint> points)
        {
            throw new NotImplementedException();
        }
        */
        public int getTopCell()
        {
            return topCell;
        }

        public int getBottomCell()
        {
            return bottomCell;
        }

        public int getLeftCell()
        {
            return leftCell;
        }

        public int getRightCell()
        {
            return rightCell;
        }


        public void setTopLinkedGridIntersectPoint(IEnumerable<LinkedPoint> points)
        {
            this._upLinkedPoints = points as List<LinkedPoint>;
        }

        public void setBottomLinkedGridIntersectPoint(IEnumerable<LinkedPoint> points)
        {
            this._downLinkedPoints = points as List<LinkedPoint>;
        }

        public void setLeftLinkedGridIntersectPoint(IEnumerable<LinkedPoint> points)
        {
            this._leftLinkedPoints = points as List<LinkedPoint>;
        }

        public void setRightLinkedGridIntersectPoint(IEnumerable<LinkedPoint> points)
        {
            this._rightLinkedPoints = points as List<LinkedPoint>;
        }
        public  IEnumerable<PostPolygon> getPostPolygons(){
            return this.postPolys;
        }
    }

}
